
#include "./container-impl.h"
#include "common/auto-lock.h"
#include "common/list-dir.h"
#include <string.h>

namespace fasmio { namespace container {

const char* const kPluginDirectory = "plugin";
const char* const kPluginExtension = ".so";

ContainerImpl::ContainerImpl(ILogger* logger) :
    logger_(logger),
    mlogger_(logger, "Container"),
    plugins_(),
    plugin_services_map_(),
    service_plugin_map_(),
    service_creator_map_(),
    plugins_lock_(fiber_env::NewMutex()),
    service_agents_(),
    service_agents_lock_(fiber_env::NewMutex())
{
}

ContainerImpl::~ContainerImpl()
{
}

bool ContainerImpl::Initialize()
{
    mlogger_.Info("Initializing Container ...");

    if (!load_plugins())
        return false;

    std::vector<std::string> startup_services;
    if (!determine_startup_services(&startup_services))
        return false;
    if (!start_services(startup_services))
        return false;

    mlogger_.Info("Container initialized");
    return true;
}

void ContainerImpl::Finalize()
{
    mlogger_.Info("Finalizing Container ...");

    stop_services();
    unload_plugins();

    mlogger_.Info("Container finalized");
}

bool ContainerImpl::PrepareRequest(INetRequest *request)
{
    if (request == nullptr)
        return false;

    const char* url = request->GetURL();
    const char* method = request->GetMethod();
    mlogger_.Info("Preparing request: %s %s", method, url);

    // TODO: ContainerImpl::PrepareRequest
    return true;
}

bool ContainerImpl::ServeRequest(INetRequest *request, INetResponse **response)
{
    if (request == nullptr || response == nullptr)
        return false;

    const char* url = request->GetURL();
    const char* method = request->GetMethod();
    mlogger_.Info("Serving request: %s %s", method, url);

    // TODO: ContainerImpl::ServeRequest
    return true;
}

bool ContainerImpl::load_plugins()
{
    common::ListDir dir(kPluginDirectory, common::ListDir::OMIT_DOT | common::ListDir::FILES);
    std::string fullname, shortname;
    const unsigned int extension_len = strlen(kPluginExtension);

    common::AutoLock autolock(plugins_lock_);

    while (dir.Next(&fullname, &shortname, nullptr))
    {
        if (shortname.size() < extension_len)
            continue;
        if (0 != shortname.compare(shortname.size() - extension_len, std::string::npos, kPluginExtension))
            continue;

        mlogger_.Info("Found plugin %s", shortname.c_str());

        std::set<std::string> &services = plugin_services_map_[shortname];
        plugin_ptr_t plugin(new PluginImpl(logger_, fullname.c_str()));
        plugins_[shortname] = plugin;

        const service_creator_map_t &creators = plugin->DiscoverAvailableServices();
        if (creators.empty())
            mlogger_.Info("No service found in plugin %s: %s", shortname.c_str(), plugin->GetEmptyReason());
        else
        {
            for (service_creator_map_t::const_iterator iter = creators.begin();
                iter != creators.end(); ++iter)
            {
                const std::string &service = iter->first;
                service::ServiceCreator creator = iter->second;

                services.insert(service);
                service_plugin_map_[service] = plugin;
                service_creator_map_[service] = creator;
                mlogger_.Info("Service %s found", service.c_str());
            }
        }
    }
    mlogger_.Info("Totally %d plugins, %d services found", plugins_.size(), service_plugin_map_.size());

    return true;
}

bool ContainerImpl::determine_startup_services(std::vector<std::string> *services)
{
    if (services == nullptr)
        return false;

    // TODO: ContainerImpl::determine_startup_services
    services->push_back("registry");


    mlogger_.Info("Totally %d startup services configured", services->size());
    for (std::vector<std::string>::iterator iter = services->begin();
        iter != services->end(); ++iter)
    {
        mlogger_.Info("Startup service: %s", iter->c_str());
    }

    return true;
}

bool ContainerImpl::start_services(const std::vector<std::string> &services)
{
    // TODO: ContainerImpl::start_services
    return true;
}

void ContainerImpl::stop_services()
{
    // TODO: ContainerImpl::stop_services
}

void ContainerImpl::unload_plugins()
{
    // TODO: ContainerImpl::unload_plugins
}

}}  // namespace fasmio::container

